/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hhf.flex.ascreator.creators;

import hhf.flex.annotations.services.GenericService;
import hhf.flex.annotations.services.OfflinePolicyEnum;
import hhf.flex.ascreator.ASAbstractServiceCreator;
import hhf.flex.ascreator.ASDelayedServiceCreator;
import hhf.flex.ascreator.exceptions.IllegalAnnotationException;
import hhf.flex.ascreator.patterns.ASDelayedFunctionPattern;
import hhf.flex.ascreator.patterns.ASPattern;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author utilisateur
 */
public class ASGenericServiceCreator extends ASAbstractServiceCreator {

	//OfflinePolicy du service
	protected OfflinePolicyEnum offlinePolicy;

	@Override
	public void init(final String path, final Method method, final String id, final Map<String, Object> options) throws IllegalAnnotationException {
		super.init(path, method, id, options);
		this.offlinePolicy = method.getAnnotation(GenericService.class).offlinePolicy();

	}

	/**
	 * Ecrit une methode AS disant si les données en base sont périmées.
	 * isDatasOutOfDateMethod(" + argsTyped + ")
	 * @param out
	 * @throws IOException
	 */
	@Override
	protected void writeIsDatasOutOfDateBody(OutputStreamWriter out) throws IOException {
		out.write("\t\t\treturn true;\n");
	}

	/**
	 * Ecrit la methode qui appel le service distant
	 * send(" + argsTyped + "):void
	 * @param out
	 * @throws IOException
	 */
	@Override
	protected void writeSendBody(OutputStreamWriter out) throws IOException {
		ASPattern.writeDefaultSendBody(out, arguments, cancelFunctionsNames, chainLoadFunctionsNames, writeRemoveEntitiesFunction, writeAddEntitiesFunction, argNames, offlinePolicy, getASClassName(), getASPackage(), method.getName());
	}

	/**
	 * Ecrit la methode qui recoit le resultat du reseau
	 * resultNetworkHandler(event:ResultEvent):void
	 * @param out
	 * @throws IOException
	 */
	@Override
	protected void writeResultNetworkHandlerBody(OutputStreamWriter out) throws IOException {
	}

	
	@Override
	protected void writeDelayFunctionEntities(OutputStreamWriter out) throws IOException {
		if (this.offlinePolicy == OfflinePolicyEnum.DELAY) {
			ASDelayedServiceCreator delayedServiceCreator = new ASDelayedServiceCreator(method, this.imports, path, getASPackage(), className, argsTyped);
			delayedServiceCreator.write();
		}
	}

	@Override
	protected void writeLaunchDelayedServices(OutputStreamWriter out) throws IOException {
		//on ecrit la fonction de lancement des services "DELAYED" si 
		//telle est la politique définie
		if (this.offlinePolicy == OfflinePolicyEnum.DELAY) {
			ASDelayedFunctionPattern.writeLaunch(out, path, className, id, argsTyped, imports, method);
		}
	}

	@Override
	protected void writeASFieldsAndMethods(OutputStreamWriter out) throws IOException {

		String collectionOf = null;
		if ("Array".equals(returnType)) {
			Type genType = method.getGenericReturnType(); // c'est une Map ou une Collection
			collectionOf = getASArrayElementType(genType);
		}
		writeDatasGetterSetter(out, collectionOf);

		// UPDATEAVAILABILITY
		out.write("\t\t/**\n");
		out.write("\t\t * Que se passe t'il quand l'état du réseau change.\n");
		out.write("\t\t */\n");
		out.write("\t\tprotected override function updateAvailability(event:StatusEvent):void {\n");
		writeUpdateAvailabilityBody(out);
		out.write("\t\t}\n\n");


		// RESULTNETWORKHANDLER
		out.write("\t\t/**\n");
		out.write("\t\t * Un résultat est survenue du réseau\n");
		out.write("\t\t */\n");
		out.write("\t\tprotected override function resultNetworkHandler(event:ResultEvent):void {\n");
		out.write("\t\t\tvar obj:" + className + " = (event.target as " + className + ");\n");
		if (returnType.equals("void")) {
		} else if (returnType.equals("int")) {
			out.write("\t\t\tobj._datas = NaN;\n");
		} else if (returnType.equals("Boolean")) {
			out.write("\t\t\tobj.datas = !obj._datas;\n");
		} else {
			out.write("\t\t\tobj._datas = null;\n");
		}
		out.write("\t\t\tvar args:Array = new Array();\n");
		out.write("\t\t\targs.push(event.result);\n");
		out.write("\t\t\targs.push.apply(args, event.token.message.body);\n");
		writeResultNetworkHandlerBody(out); // c'est dans cette methode que l'on decide de sauvegarder en base ou pas
		if (!"void".equals(returnType)) {
			out.write("\t\t\tobj.datas = event.result as " + returnType + "; // on set le resultat\n");
		}
		out.write("\t\t}\n\n");

		//LINKEDSERVICESFUNCTIONS
		//Laisser cet appel avant le writeSendBody
		//pour que les appels des fonctions cancel soient écrites
		//dans le send
		writeLinkedServicesFunctions(out);

		// SEND
		out.write("\t\t/**\n");
		out.write("\t\t * Appelle de la methode\n");
		out.write("\t\t */\n");
		out.write("\t\tpublic function send(" + argsTyped + "):void {\n");
		writeSendBody(out);
		out.write("\t\t}\n\n");

		//LOADINLOCALEBASE
		out.write("\t\tpublic function loadInLocaleBase(" + argsTyped + "):void {\n");
		ASPattern.writeLoadInLocaleBase(out, arguments);
		out.write("\t\t}\n\n");

		// ISDATEOUTOFDATE
		out.write("\t\t/**\n");
		out.write("\t\t * Retourne le temps écoulé depuis la derniere mise à jour.\n");
		out.write("\t\t */\n");
		out.write("\t\tprivate function isDatasOutOfDate(" + argsTyped + "):Boolean {\n");
		writeIsDatasOutOfDateBody(out);
		out.write("\t\t}\n\n");

		//REMOVEENTITIES 		
		writeRemoveEntitiesFunctions(out);

		//ADDENTITIES
		writeAddEntitiesFunctions(out);

		//DELAYFUNCTIONS
		writeDelayFunctionEntities(out);
		writeLaunchDelayedServices(out);
		writeCheckDelayedServices(out);

	}

	@Override
	protected Set<String> getASImports() {
		Set<String> imports = new HashSet<String>();
		imports.add("flash.utils.ByteArray");
		imports.add("mx.resources.ResourceManager");
		imports.add("flash.events.StatusEvent");
		imports.add("hhf.services.AbstractServiceResponder");
		imports.add("hhf.net.NetworkMonitor");
		imports.add("mx.rpc.events.ResultEvent");
		imports.add("mx.rpc.events.FaultEvent");
		imports.add("mx.rpc.Fault");
		imports.add("mx.core.mx_internal");
		imports.add("mx.collections.ArrayCollection");
		imports.add("mx.utils.ObjectUtil");
		imports.add("mx.rpc.AbstractOperation");
		imports.add("mx.rpc.AbstractService");
		imports.add("mx.rpc.AsyncToken");
		imports.add("mx.rpc.remoting.Operation");
		imports.add("mx.rpc.remoting.RemoteObject");
		imports.add("mx.utils.UIDUtil");
		imports.add("mx.controls.Alert");
		imports.add("flex.persistence.criteria.CriteriaBuilder");
		imports.add("flex.persistence.criteria.CriteriaQuery");
		imports.add("flex.persistence.criteria.Predicate");
		imports.add("flex.persistence.criteria.Root");
		imports.add("flex.persistence.Query");
		imports.add("flex.persistence.exceptions.NoResultException");
		imports.add("flex.persistence.exceptions.EntityNotFoundException");
		imports.add("flex.persistence.exceptions.EntityExistsException");
		imports.add("hhf.services.entities.DelayedClasses");
		imports.add("flash.utils.getDefinitionByName");

		/*imports.add("hhf.services.entities.DelayedFunction"); */
		imports.addAll(ASPattern.getAnnotationImports(method));
		getASImportsFromMethod(method, imports);		
		this.imports = imports;
		return imports;
	}

}
